package com.mfhcd.facepay.interceptor;

import android.os.Build;

import androidx.annotation.RequiresApi;

import com.mfhcd.facepay.model.BaseRequestModel;
import com.mfhcd.facepay.constant.SignConstant;
import com.mfhcd.facepay.global.AppConfig;
import com.mfhcd.facepay.utils.DateUtils;
import com.mfhcd.facepay.utils.LogUtil;
import com.mfhcd.facepay.utils.SPUtils;
import com.mfhcd.facepay.utils.SignatureUtil;

import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;

/**
 * @Author：张洪顺
 * @E-Mail: zhanghs@mfhcd.com
 * @Date：2019-10-11 16:03
 * @版权所有 © 现代金融控股（成都）有限公司
 * @描述：Paas网络请求拦截器
 */
public class PassHttpLoggingInterceptor implements Interceptor {
    public static final Charset UTF8 = Charset.forName("UTF-8");

    /**
     * 拦截请求
     * 1、增加公共请求头
     * 2、打印调试日志
     *
     * @param chain
     * @return
     * @throws IOException
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Request request = chain.request();
        if(!request.body().contentType().type().equals("multipart")) {//multipart 例如文件上传不需要转json, 直接请求
            Request.Builder requestBuilder = request.newBuilder();
            // 创建新header添加公共头部参数
            Headers.Builder headerBuilder = request.headers().newBuilder();
            // 请求参数
            String requestString = bodyToString(request);
            try {
                JSONObject requestObj = new JSONObject(requestString);
                BaseRequestModel baseRequestModel = new BaseRequestModel.PassCommonReq(requestObj);
                String sign = SignatureUtil.paasSign(baseRequestModel);
                headerBuilder
                        .add(SignConstant.HEADER_CONTENT_TYPE_LABEL, SignConstant.HEADER_CONTENT_TYPE_JSON)
                        .add(SignConstant.HEADER_TIME_LABEL, DateUtils.getUTCTime())
                        .add(SignConstant.HEADER_SIGN, sign)
                        .add(SignConstant.HEADER_TOKEN, SPUtils.getToken())
                        .build();
                requestBuilder.headers(headerBuilder.build());
                request = requestBuilder.build();
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        //不打任何日志
        if (!AppConfig.DEBUG) {
            return chain.proceed(request);
        }

        //请求日志拦截
        logForRequest(request, chain.connection());

        //执行请求，计算请求时间
        long startNs = System.nanoTime();
        Response response;
        try {
            response = chain.proceed(request);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.d("<-- HTTP FAILED: " + e.getMessage());
            throw e;
        }
        long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);

        //响应日志拦截
        return logForResponse(response, tookMs);
    }

    /**
     * 记录请求日志
     *
     * @param request
     * @param connection
     * @throws IOException
     */
    protected void logForRequest(Request request, Connection connection) throws IOException {
        if (AppConfig.DEBUG) {
            LogUtil.d("-------------------------------request-------------------------------");
        }
        boolean logBody = AppConfig.DEBUG;
        boolean logHeaders = AppConfig.DEBUG;
        RequestBody requestBody = request.body();
        boolean hasRequestBody = requestBody != null;
        Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;

        try {
            String requestStartMessage = "--> BEGIN " + request.method() + ' ' + request.url() + ' ' + protocol;
            LogUtil.d(requestStartMessage);

            if (logHeaders) {
                Headers headers = request.headers();
                for (int i = 0, count = headers.size(); i < count; i++) {
                    LogUtil.d("\t" + headers.name(i) + ": " + headers.value(i));
                }
            }

            if (logBody && hasRequestBody) {
                if (isPlaintext(requestBody.contentType())) {
                    LogUtil.d("\tbody:" + bodyToString(request));
                } else {
                    LogUtil.d("\tbody: maybe [file part] , too large too print , ignored!");
                }
            }
        } catch (Exception e) {
            onError(e);
        } finally {
            if (AppConfig.DEBUG) {
                LogUtil.d("--> END " + request.method());
            }
        }
    }

    /**
     * 记录响应日志
     *
     * @param response
     * @param tookMs   请求花费的时间
     * @return
     */
    protected Response logForResponse(Response response, long tookMs) {
        if (AppConfig.DEBUG) {
            LogUtil.d("-------------------------------response-------------------------------");
        }
        Response.Builder builder = response.newBuilder();
        Response clone = builder.build();
        ResponseBody responseBody = clone.body();
        boolean logBody = AppConfig.DEBUG;
        boolean logHeaders = AppConfig.DEBUG;

        try {
            LogUtil.d("<-- " + clone.code() + ' ' + clone.message() + ' ' + clone.request().url() + " (" + tookMs + "ms）");
            if (logHeaders) {
                LogUtil.d(" ");
                Headers headers = clone.headers();
                for (int i = 0, count = headers.size(); i < count; i++) {
                    LogUtil.d("\t" + headers.name(i) + ": " + headers.value(i));
                }
                LogUtil.d(" ");
            }

            if (logBody && HttpHeaders.hasBody(clone)) {
                if (isPlaintext(responseBody.contentType())) {
                    String body = responseBody.string();
                    LogUtil.d("\tbody:" + body);
                    responseBody = ResponseBody.create(responseBody.contentType(), body);
                    return response.newBuilder().body(responseBody).build();
                } else {
                    LogUtil.d("\tbody: maybe [file part] , too large too print , ignored!");
                }
                if (AppConfig.DEBUG) {
                    LogUtil.d(" ");
                }
            }
        } catch (Exception e) {
            onError(e);
        } finally {
            if (AppConfig.DEBUG) {
                LogUtil.d("<-- END HTTP");
            }
        }
        return response;
    }


    protected String bodyToString(Request request) {
        try {
            final Request copy = request.newBuilder().build();
            final Buffer buffer = new Buffer();
            copy.body().writeTo(buffer);
            Charset charset = UTF8;
            MediaType contentType = copy.body().contentType();
            if (contentType != null) {
                charset = contentType.charset(UTF8);
            }
            return URLDecoder.decode(buffer.readString(charset), UTF8.name());
        } catch (Exception e) {
            onError(e);
        }
        return "";
    }

    /**
     * 判断请求响应内容是类型
     */
    private boolean isPlaintext(MediaType mediaType) {
        if (mediaType == null) {
            return false;
        }
        if (mediaType.type() != null && mediaType.type().equals("text")) {
            return true;
        }
        String subtype = mediaType.subtype();
        if (subtype != null) {
            subtype = subtype.toLowerCase();
            return subtype.contains("x-www-form-urlencoded") ||
                    subtype.contains("json") ||
                    subtype.contains("xml") ||
                    subtype.contains("html");
        }
        return false;
    }


    protected void onError(Throwable t) {
        if (AppConfig.DEBUG) {
            t.printStackTrace();
        }
    }

}
